﻿//*****************************************************************************
// Copyright (C) 2013 Cognex Corporation
//
// Subject to Cognex Corporation's terms and conditions and license
// agreement, you are authorized to use and modify this source code in
// any way you find useful, provided the Software and/or the modified
// Software is used solely in conjunction with a Cognex Machine Vision
// System.  Furthermore you acknowledge and agree that Cognex has no
// warranty, obligations or liability for your use of the Software.
//*****************************************************************************
// This sample program is designed to illustrate certain VisionPro
// features or techniques in the simplest way possible. It is not
// intended as the framework for a complete application. In particular,
// the sample program may not provide proper error handling, event
// handling, cleanup, repeatability, and other mechanisms that a
// commercial quality application requires.
//
// This program assumes that you have some knowledge of C# and VisionPro
// programming.
//
// This sample demonstrates how to modify properties, and acquire
// Intensity and Range images using the Profile Camera.
//


using System;
using System.Windows.Forms;
using Cognex.VisionPro;
using Cognex.VisionPro.FGGigE;
using Cognex.VisionPro.Exceptions;
using System.Threading;
using System.IO;

namespace ProfileCameraAcquisition
{
    public partial class Form1 : Form
    {
        // Delegates to help thread-safe control manipulation
        public delegate void messageTextBoxAppendDelegate(string s);        
        public delegate void setInputImageDelegate(ICogImage img);
        public delegate void enableProfileCamControlsDelegate(bool enabled);
        public delegate void displayErrorLabelDelegate(bool visible, string text);

        // Profile Camera helper object
        private ProfileCameraHelper mProfileCamHelper = null;
        private bool mIsFormShown = false;
        private bool mErrorShown = false;
        
        // Time to display the acq error label in ms
        private readonly int errorLabelDisplayTimeMS = 1500;

        /// <summary>
        /// Form constructor
        /// </summary>
        public Form1()
        {
            InitializeComponent();
            this.mCogDisplayStatusBar.Display = mCogDisplay;

            ICogAcqFifo profileCamFifo = null;

            // Create acquisition fifo for the first Profile Camera framegrabber
            CogFrameGrabberGigEs fgs = new CogFrameGrabberGigEs();
            foreach (ICogFrameGrabber fg in fgs)
            {
                // Identifying profile cameras
                // Checking for the available video format allows for finding any Cognex profile camera
                // but blocks the cameras from using them in other applications. To avoid this, you can
                // check for the specific name of the framegrabber instead, or use a regexp: "DS1[1-9]00"
                try
                {
                    MessageTextBoxAppend("Profile camera found, trying to connect...");
                    if (fg.AvailableVideoFormats[0] == "Cognex NullFormat")
                    {
                        profileCamFifo = fg.CreateAcqFifo(fg.AvailableVideoFormats[0],
                                                     CogAcqFifoPixelFormatConstants.Format16Grey,
                                                     0,
                                                     true);
                    }
                }
                catch (CogAcqHardwareInUseException)
                {
                    // Profile Camera is busy
                    MessageTextBoxAppend("Profile camera detected, but is busy!");
                    EnableProfileCameraControls(false);
                    profileCamFifo = null;
                    continue;
                }
                catch (Exception ex)
                {
                    // Other error
                    MessageTextBoxAppend("Profile camera initialization error: " + ex.Message);
                    EnableProfileCameraControls(false);
                    profileCamFifo = null;
                    continue;
                }

                if (profileCamFifo != null)
                {
                    break;
                }
            }

            if (profileCamFifo != null)
            {
                // Create helper object
                mProfileCamHelper = new ProfileCameraHelper(profileCamFifo);

                // Event subscriptions
                mProfileCamHelper.ImageReady += mProfileCamHelper_ImageReady;
                mProfileCamHelper.AsynchronousAcqError += mProfileCamHelper_AsynchronousAcqError;

                // Camera configuration
                int x, y, w, h;
                mProfileCamHelper.ProfileCamFifo.OwnedROIParams.GetROIXYWidthHeight(out x, out y, out w, out h);
                mProfileCamHelper.ProfileCamFifo.OwnedROIParams.SetROIXYWidthHeight(x, y, w, 500);
                mProfileCamHelper.ProfileCamFifo.Timeout = 5000;

                // Print GUI messages
                txt_Message.AppendText("Profile Camera detected!" + Environment.NewLine);
                txt_Message.AppendText(" Name: " + profileCamFifo.FrameGrabber.Name + Environment.NewLine);
                txt_Message.AppendText(" Serial: " + profileCamFifo.FrameGrabber.SerialNumber + Environment.NewLine);
                txt_Message.AppendText(" Videoformat: " + profileCamFifo.VideoFormat + Environment.NewLine);
                EnableProfileCameraControls(true);
            }
            else
            {
                txt_Message.AppendText("Profile Camera could not be found, or is busy!" + Environment.NewLine);
                EnableProfileCameraControls(false);
            }
        }

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }

        /// <summary>
        /// Syncronously acquire image in a separate method
        /// </summary>
        public void AcquireImageMethod()
        {
            EnableProfileCameraControls(false);

            try
            {
                mProfileCamHelper.SyncAcquire();
            }
            catch (CogAcqEncoderOverrunException)
            {
                // Encoder is too fast!
                MessageTextBoxAppend("Encoder overrun occured!");
                ShowError("Encoder overrun occured!", errorLabelDisplayTimeMS);
                
                return;
            }
            catch (CogAcqTimeoutException)
            {
                // Acquisition timeout!
                MessageTextBoxAppend("Acquisition timeout elapsed (possible reasons: timeout too short, trigger not detected or encoder too slow/not detected");
                ShowError("Acquisition timeout elapsed.", -1);
                return;
            }
            catch (Exception ex)
            {
                // Another error occured!
                MessageTextBoxAppend("Acquisition error: " + ex.Message);
                ShowError("Acquisition error.", -1);
                return;
            }
            finally
            {
                EnableProfileCameraControls(true);
            }
        }

        #region GUI helper methods
        /// <summary>
        /// Enable or disable camera GUI controls
        /// </summary>
        /// <param name="enabled">The enabled property of controls is set to this value</param>
        private void EnableProfileCameraControls(bool enabled)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new enableProfileCamControlsDelegate(this.EnableProfileCameraControls), enabled);
            }
            else
            {
                pnl_CameraMode.Enabled = enabled;
                btn_SyncAcquire.Enabled = enabled;
                ckb_Continuous.Enabled = enabled;
                pnl_ImageSourceSettings.Enabled = enabled;
            }
        }

        /// <summary>
        /// Append text to txt_Message textbox
        /// </summary>
        /// <param name="s">String to append</param>
        public void MessageTextBoxAppend(string s)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new messageTextBoxAppendDelegate(this.MessageTextBoxAppend), s);
            }
            else
            {
                DateTime now = DateTime.Now;
                this.txt_Message.AppendText("[" + now.Hour.ToString("D2") + ":" +
                                                  now.Minute.ToString("D2") + ":" +
                                                  now.Second.ToString("D2") + "] " + s + Environment.NewLine);
            }
        }

        /// <summary>
        /// Set the input image of the CogDisplay on the control (and zoom to fit)
        /// </summary>
        /// <param name="img">Image to be set as inputimage for the CogDisplay</param>
        public void SetInputImage(ICogImage img)
        {
            if (this.InvokeRequired)
            {
                // Catching the ObjectDisposedException is required, because upon closing the form,
                // a race condition emerges between the form disposal and the Invoke request from
                // the acquisition thread to the GUI thread. There is no trivial way to avoid this, but
                // to catch and suppress the arising exception
                try
                {
                    this.Invoke(new setInputImageDelegate(this.SetInputImage), img);
                }
                catch (ObjectDisposedException)
                {
                    // Do nothing here
                }
            }
            else
            {
                // Set input image and zoom to fit
                Measure_high(img.ToBitmap());
                this.mCogDisplay.Image = img;
                this.mCogDisplay.Fit(true);
            }
        }

        /// <summary>
        /// Show or hide error label on the form
        /// </summary>
        /// <param name="visible">True to display, false to hide the label</param>
        /// <param name="msg">Error message to show on the label</param>
        public void DisplayErrorLabel(bool visible, string msg)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new displayErrorLabelDelegate(this.DisplayErrorLabel), visible, msg);
            }
            else
            {
                this.lbl_AcqError.Visible = visible;
                this.lbl_AcqError.Text = msg;
            }
        }

        /// <summary>
        /// Displays the error label using a non-blocking thread with a specified text, for a 
        /// specified time period
        /// </summary>
        /// <param name="errorMsg">Message to display in the label</param>
        /// <param name="timeMS">Time period to display the label for</param>
        public void ShowError(string errorMsg, int timeMS)
        {
            if (mErrorShown && lbl_AcqError.Text == errorMsg)
            {
                return;
            }
            else
            {
                // Create a separate thread to avoid blocking the GUI thread.
                Thread t = new Thread(delegate()
                    {
                        mErrorShown = true;
                        DisplayErrorLabel(true, errorMsg);
                        if (timeMS == -1)
                        {
                            return;
                        }
                        Thread.Sleep(timeMS);
                        DisplayErrorLabel(false, "");
                        mErrorShown = false;
                    }
                );
                t.Start();
            }
        }
        #endregion

        #region Event-handlers
        // Event-handler hooked to the CameraMode radiobuttons
        private void rdb_CameraMode_CheckedChanged(object sender, EventArgs e)
        {
            if (rdb_InstensityWG.Checked)
            {
                mProfileCamHelper.ProfileCamFifo.OwnedProfileCameraParams.CameraMode = CogAcqCameraModeConstants.IntensityWithGraphics;
            }
            else
            {
                mProfileCamHelper.ProfileCamFifo.OwnedProfileCameraParams.CameraMode = CogAcqCameraModeConstants.Range;
            }
        }

        // Event-handler hooked to synchronous acq start button
        private void btn_SyncAcquire_Click(object sender, EventArgs e)
        {
            // Clear error label if remained there
            if (mErrorShown)
            {
                mErrorShown = false;
                DisplayErrorLabel(false, "");
            }

            if (rdb_InstensityWG.Checked)
            {
                this.AcquireImageMethod();
            }
            else
            {
                // Asyncronous acquisition for range image to keep GUI responsive
                Thread t = new Thread(this.AcquireImageMethod);
                t.Start();
            }
        }

        // Event-handler hooked to RunContinuous Checkbox (button appearance)
        private void ckb_Continuous_CheckedChanged(object sender, EventArgs e)
        {
            // Clear error message on the GUI if necessary
            if (ckb_Continuous.Checked && mErrorShown)
            {
                mErrorShown = false;
                DisplayErrorLabel(false, "");
            }

            // Disable / enable controls
            rdb_InstensityWG.Enabled = !ckb_Continuous.Checked;
            rdb_Range.Enabled = !ckb_Continuous.Checked;
            btn_SyncAcquire.Enabled = !ckb_Continuous.Checked;
            ckb_TestEncoder.Enabled = !ckb_Continuous.Checked;

            if (ckb_Continuous.Checked)
            {
                mProfileCamHelper.ProfileCamFifo.OwnedTriggerParams.TriggerModel = CogAcqTriggerModelConstants.FreeRun;
            }
            else
            {
                mProfileCamHelper.ProfileCamFifo.OwnedTriggerParams.TriggerModel = CogAcqTriggerModelConstants.Manual;
            }
        }

        // Event-handler to turn test-encoder on and off
        private void ckb_TestEncoder_CheckedChanged(object sender, EventArgs e)
        {
            mProfileCamHelper.ProfileCamFifo.OwnedLineScanParams.TestEncoderEnabled = ckb_TestEncoder.Checked;
        }

        // Event-handler to update display when image is ready
        private void mProfileCamHelper_ImageReady(object sender, EventArgs e, ICogImage img)
        {
            // Clear error message on the GUI if successful acq happens
            if (mErrorShown)
            {
                mErrorShown = false;
                DisplayErrorLabel(false, "");
            }
            this.SetInputImage(img);
            //Thread.Sleep(500);
            //Measure_high(img.ToBitmap());


            // Call the garbage collector to free up old images that are no longer needed.
            GC.Collect();
        }

        // Event-handler to display error message when asyncronous acq fails
        private void mProfileCamHelper_AsynchronousAcqError(object sender, string errorMsg)
        {
            this.MessageTextBoxAppend(errorMsg);
            this.ShowError(errorMsg, errorLabelDisplayTimeMS);
        }

        // Event-handler to keep values of NumUpDown controls and camera properties in sync
        // This event-handler is subscribed to all three NumericUpDown controls' changed event for the sake of simplicity.
        private void nud_CameraProperty_ValueChanged(object sender, EventArgs e)
        {
            // Return immediately if form initialization is not complete.  This prevents writing GUI values to
            // the camera before they have been initialized.
            if (!mIsFormShown)
            {
                return;
            }

            try
            {
                if (sender == nud_ZBase || sender == nud_ZHeight)
                {
                    // Validate ZDetection value
                    if ((double)(nud_ZHeight.Value + nud_ZBase.Value) > mProfileCamHelper.ProfileCamFifo.OwnedProfileCameraParams.ZDetectionHeightMax)
                    {
                        nud_ZBase.Value = (decimal)mProfileCamHelper.ProfileCamFifo.OwnedProfileCameraParams.ZDetectionBase;
                        nud_ZHeight.Value = (decimal)mProfileCamHelper.ProfileCamFifo.OwnedProfileCameraParams.ZDetectionHeight;
                    }
                    else
                    {
                        mProfileCamHelper.ProfileCamFifo.OwnedProfileCameraParams.ZDetectionBase = (double)nud_ZBase.Value;
                        mProfileCamHelper.ProfileCamFifo.OwnedProfileCameraParams.ZDetectionHeight = (double)nud_ZHeight.Value;
                    }
                }
                else if (sender == nud_StepsPerLine)
                {
                    // Apply steps per line setting to camera
                    int previousValue = (int)nud_StepsPerLine.Value;
                    try
                    {
                        mProfileCamHelper.ProfileCamFifo.OwnedLineScanParams.SetStepsPerLine((int)nud_StepsPerLine.Value, 0);
                    }
                    catch(Exception ex)
                    {
                        // Need to restore value on error, because NumericUpDown boundaries might be incorrect
                        mProfileCamHelper.ProfileCamFifo.OwnedLineScanParams.SetStepsPerLine(previousValue, 0);
                        nud_StepsPerLine.Value = previousValue;
                        throw new Exception(ex.Message);
                    }
                }
                else if (sender == nud_RangeHeight)
                {
                    // Need to restore value, because 
                    // Apply range image ROI setting to camera
                    int x, y, w, h;
                    mProfileCamHelper.ProfileCamFifo.OwnedROIParams.GetROIXYWidthHeight(out x, out y, out w, out h);
                    try
                    {
                        // Apply steps per line setting to camera
                        mProfileCamHelper.ProfileCamFifo.OwnedROIParams.SetROIXYWidthHeight(x, y, w, (int)nud_RangeHeight.Value);
                    }
                    catch (Exception ex)
                    {
                        // Need to restore value on error, because NumericUpDown boundaries might be incorrect
                        nud_RangeHeight.Value = h;
                        mProfileCamHelper.ProfileCamFifo.OwnedROIParams.SetROIXYWidthHeight(x, y, w, h);
                        throw new Exception(ex.Message);
                    }
                }
            }
            catch (Exception ex)
            {
                MessageTextBoxAppend("Camera configuration error: " + ex.Message);
            }
        }

        // Event-handler for form closing to dispose Profile Camera helper
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            var fgs = new CogFrameGrabbers();
            foreach (ICogFrameGrabber fg in fgs)
            {
                fg.Disconnect(false);
            }
            if (mProfileCamHelper != null)
            {
                mProfileCamHelper.Dispose();
            }
        }

        // Event-handler to initialize control values, and set flag after form is shown.
        // Flag is required to prevent other event-handlers from running to early 
        private void Form1_Shown(object sender, EventArgs e)
        {
            // Set the GUI controls to the property values read from the camera
            if (mProfileCamHelper != null)
            {
                nud_ZBase.Value = (decimal)mProfileCamHelper.ProfileCamFifo.OwnedProfileCameraParams.ZDetectionBase;
                nud_ZHeight.Value = (decimal)mProfileCamHelper.ProfileCamFifo.OwnedProfileCameraParams.ZDetectionHeight;
                
                int spl, spl16;
                mProfileCamHelper.ProfileCamFifo.OwnedLineScanParams.GetStepsPerLine(out spl, out spl16);
                nud_StepsPerLine.Value = spl;

                int x, y, w, h;
                mProfileCamHelper.ProfileCamFifo.OwnedROIParams.GetROIXYWidthHeight(out x, out y, out w, out h);
                nud_RangeHeight.Value = (decimal)h;

                lbl_SizeSpec.Text = "Range size: " + w + " x [userdefined]";
            }
            mIsFormShown = true;
        }
        #endregion

        private void btn_measure_Click(object sender, EventArgs e)
        {
            // Clear error label if remained there
            if (MessageBox.Show("是否清除数据", "Confirm Message", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
            {
                dataGridView1.Rows.Clear();
            }

        }
        private void Measure_high(System.Drawing.Bitmap bitmap)
        {
            
            //获取上面红点最高值
            int point1x = 0;
            int point1y = 0;

            System.Drawing.Color color = bitmap.GetPixel(0, 0);
            for (int i = 0; i < bitmap.Height; i++)
            {
                int j = 0;
                for (;j< bitmap.Width;j++)
                {
                    color = bitmap.GetPixel(j,i);
                    if(color.R > 55)
                    {
                        //for (int K = 0; K < bitmap.Width; K++)//整行设为红色
                        //{
                        //    bitmap.SetPixel(K, i, System.Drawing.Color.Red);
                        //}
                        point1x = j;
                        point1y = i;
                        break;
                    }
                }
                //color = bitmap.GetPixel(j, i);
                if (color.R > 55) break;

            }

            //获取下面红点最低值
            int point2x = 0;
            int point2y = 0;
            for (int i = bitmap.Height - 1; i > 0; i--)
            {
                int j = bitmap.Width - 1;
                for (; j > 0; j--)
                {
                    color = bitmap.GetPixel(j, i);
                    if (color.R > 55)
                    {
                        //for (int K = 0; K < bitmap.Width; K++)//整行设为红色
                        //{
                        //    bitmap.SetPixel(K, i, System.Drawing.Color.Red);
                        //}
                        point2x = j;
                        point2y = i;
                        break;
                    }
                }
                //color = bitmap.GetPixel(j, i);
                if (color.R > 55) break;
            }

            //for(int i= point1y; i< point2y; i++)//两点垂直方向设为红色
            //{
            //    bitmap.SetPixel(point1x, i, System.Drawing.Color.Red);
            //}

            //CogImage24PlanarColor cogImage24 = new CogImage24PlanarColor(bitmap);
            ////改变图像
            //this.mCogDisplay.Image = cogImage24;
            //this.mCogDisplay.Fit(true);

            float rate = ((float)correctvalue.Value);
            string text1 = Convert.ToString(point1x) + "," + Convert.ToString(point1y) + ",0";
            string text2 = Convert.ToString(point2x) + "," + Convert.ToString(point2y) + ",0";
            string text3 = Convert.ToString(point2y - point1y);
            string text4 = Convert.ToString((point2y - point1y) * rate);
            string[] row1 = new string[] { text1, text2, text3, text4};

            
            dataGridView1.Rows.Add(row1);

        }

        //string saveFileName = "";
        //private void button1_Click(object sender, EventArgs e)
        //{
        //    string fileName = "";
            
        //    SaveFileDialog saveDialog = new SaveFileDialog();
        //    saveDialog.DefaultExt = "xlsx";
        //    saveDialog.Filter = "Excel文件|*.xlsx";
        //    saveDialog.FileName = fileName;
        //    saveDialog.ShowDialog();
        //    saveFileName = saveDialog.FileName;
        //    if (saveFileName.IndexOf(":") < 0)
        //        return; //被点了取消
        //}

        //保存数据到csv文件
        private void button1_Click(object sender, EventArgs e)
        {
            //实例化SaveFileDialog
            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.Filter = "Execl files (*.csv)|*.csv";
            saveFileDialog.FilterIndex = 0;
            saveFileDialog.RestoreDirectory = true;
            saveFileDialog.CreatePrompt = true;
            saveFileDialog.Title = "保存为csv文件";

            //打开SaveFileDialog控件
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                //数据流变量
                Stream myStream;
                //返回SaveFileDialog控件，并将所选择的文件转化成流
                myStream = saveFileDialog.OpenFile();
                //将选择的文件流生成写入流
                StreamWriter sw = new StreamWriter(myStream, System.Text.Encoding.GetEncoding(-0));
                string columnTitle = "";
                try
                {
                    //写入列标题
                    for (int i = 0; i < dataGridView1.ColumnCount; i++)
                    {
                        if (i > 0)
                        {
                            columnTitle += ",";
                        }
                        columnTitle += dataGridView1.Columns[i].HeaderText;
                    }
                    //将内容写入文件流中
                    sw.WriteLine(columnTitle);

                    for (int j = 0; j < dataGridView1.Rows.Count; j++)
                    {
                        string columnValue = "";
                        for (int k = 0; k < dataGridView1.Columns.Count; k++)
                        {
                            if (k > 0)
                            {
                                columnValue += ",";
                            }
                            if (dataGridView1.Rows[j].Cells[k].Value == null)
                            {
                                columnValue += "";
                            }
                            else if (dataGridView1.Rows[j].Cells[k].Value.ToString().Contains(","))
                            {
                                //将单元个中的，转义成文本
                                columnValue += "\"" + dataGridView1.Rows[j].Cells[k].Value.ToString().Trim() + "\"";
                            }
                            else
                            {
                                //横向跳格
                                columnValue += dataGridView1.Rows[j].Cells[k].Value.ToString().Trim() + "\t";
                            }
                        }
                        //内容写入文件流中
                        sw.WriteLine(columnValue);
                    }
                    sw.Close();
                    myStream.Close();
                    MessageBox.Show("导出表格成功!");
                }
                catch (Exception)
                {
                    MessageBox.Show("导出表格失败!");
                }
                finally
                {
                    sw.Close();
                    myStream.Close();
                }
            }
            else
            {
                MessageBox.Show("取消导出表格操作!");
            }
        }

    }
}
